#!/bin/bash
set -e
cd "$(dirname "$0")"

#
# This script builds the neko base image and all the applications
#

# disable buildx because of https://github.com/docker/buildx/issues/847
# if you want to use buildx, set USE_BUILDX=1
if [ -z "$USE_BUILDX" ]; then
  USE_BUILDX=0
fi

# check if docker buildx is available, its not docker-buildx command but rather subcommand of docker
#if [ -z "$USE_BUILDX" ] && [ -x "$(command -v docker)" ]; then
#  if docker buildx version >/dev/null 2>&1; then
#    USE_BUILDX=1
#  fi
#fi

function log() {
  echo "$(date +'%Y-%m-%d %H:%M:%S') - [NEKO] - $1" > /dev/stderr
}

function help() {
  echo "Usage: $0 [options] [image]"
  echo
  echo "Options:"
  echo "  -p, --platform    : The platform (default: system architecture)"
  echo "  -r, --repository  : The repository prefix (default: ghcr.io/m1k1o/neko)"
  echo "  -t, --tag         : The image tag, can be specified multiple times, if not specified"
  echo "                      uses 'latest' and if available, current git semver tag (v*.*.*)"
  echo "  -f, --flavor      : The flavor, if not specified, builds without flavor"
  echo "  -b, --base_image  : The base image name (default: <repository>[<flavor>-]base:<tag>)"
  echo "  -a, --application : The app to build, if not specified, builds the base image"
  echo "  -y, --yes         : Skip confirmation prompts"
  echo "      --no-cache    : Build without docker cache"
  echo "      --push        : Push the image to the registry after building"
  echo "  -h, --help        : Show this help message"
  echo
  echo "Positional arguments:"
  echo "  <image>           : The image name, if not specified, uses the full image name"
  echo "                      in the format <repository>/<flavor>-<application>:<tag>"
  echo "                      Example: ghcr.io/m1k1o/neko/nvidia-firefox:latest"
  echo "                      You can override any of the above options by specifying them"
  echo "                      after the image name."
  echo
  echo "Environment variables:"
  echo "  USE_BUILDX        : Set to 1 to use docker buildx instead of docker build"
  echo "                      (default: 0)"
  echo "  CLIENT_DIST       : The client dist file to use, if not specified, builds them"
  echo "                      from the source code."
  echo "  (options)         : Options can be specified as environment variables, for example:"
  echo "                      PLATFORM=linux/arm64 $0 --repository ghcr.io/m1k1o/neko"
}

FULL_IMAGE=""
while [[ "$#" -gt 0 ]]; do
  case $1 in
    --platform|-p) PLATFORM="$2"; shift ;;
    --repository|-r) REPOSITORY="$2"; shift ;;
    --tag|-t) TAGS+=("$2"); TAG="$2"; shift ;;
    --flavor|-f) FLAVOR="$2"; shift ;;
    --base_image|-b) BASE_IMAGE="$2"; shift ;;
    --application|-a) APPLICATION="$2"; shift ;;
    --yes|-y) YES=1 ;;
    --no-cache) NO_CACHE="--no-cache" log "Building without cache" ;;
    --push) PUSH=1 ;;
    --help|-h) help; exit 0 ;;
    -*) log "Unknown parameter passed: $1"; help; exit 1 ;;
    *) 
      if [ -z "$FULL_IMAGE" ]; then
        FULL_IMAGE="$1"

        # extracts image, flavor, app and tag from the full image name
        # example:
        #  ghcr.io/m1k1o/neko/nvidia-firefox:latest
        # will be split into:
        #   REPOSITORY=ghcr.io/m1k1o/neko
        #   FLAVOR=nvidia
        #   APPLICATION=firefox
        #   TAG=latest
        
        # remove the tag from the image name
        if [[ "$FULL_IMAGE" == *":"* ]]; then
          # removes everything before the last :
          TAG="${FULL_IMAGE##*:}" # will be latest
          TAGS+=("$TAG")
          # removes everything after the last :
          FULL_IMAGE="${FULL_IMAGE%:*}" # will be ghcr.io/m1k1o/neko/nvidia-firefox
        fi
        # extract the image name and save the rest to REPOSITORY
        if [[ "$FULL_IMAGE" == *"/"* ]]; then
          # removes everything after the last /
          REPOSITORY="${FULL_IMAGE%/*}" # will be ghcr.io/m1k1o/neko
          # removes everything before the last /
          FULL_IMAGE="${FULL_IMAGE##*/}" # will be nvidia-firefox
        fi
        # extract the flavor and application name
        if [[ "$FULL_IMAGE" == *"-"* ]]; then
          # removes everything after the last -
          FLAVOR="${FULL_IMAGE%-*}" # will be nvidia
          # removes everything before the last -
          APPLICATION="${FULL_IMAGE#*-}" # will be firefox
        else
          # no flavor specified so use the full image name as application name
          APPLICATION="$FULL_IMAGE" # will be firefox
        fi
        # if application name is base, set it to empty
        if [ "$APPLICATION" == "base" ]; then
          APPLICATION=""
        fi
      else
        log "Unknown positional argument: $1"
        help
        exit 1
      fi
    ;;
  esac
  shift
done

function prompt() {
  if [ ! -z "$YES" ]; then
    return 0
  fi

  local OK=""
  while [ -z "$OK" ]; do
    read -p "$1 (yes/no) " REPLY
    case "$REPLY" in
      yes|YES|y|Y)
        OK=1
        ;;
      no|NO|n|N)
        log "Aborting build."
        exit 1
        ;;
      *)
        log "Please answer 'yes' or 'no'."
        ;;
    esac
  done
}

function build_image() {
  # first argument is the image name, rest are the build args
  local APPLICATION_IMAGE="$1"
  shift

  # get list of tags in full format: <image>:<tag>
  local IMAGE_NO_TAG="${APPLICATION_IMAGE%:*}"
  local FULL_TAGS=()
  for T in "${TAGS[@]}"; do
    FULL_TAGS+=("$IMAGE_NO_TAG:$T")
  done

  if [ -z "$USE_BUILDX" ] || [ "$USE_BUILDX" != "1" ]; then
    # if buildx is not available, use docker build
    docker build \
      --platform $PLATFORM \
      $NO_CACHE \
      -t $APPLICATION_IMAGE \
      $@

    # tag and push the image to the registry
    for T in $FULL_TAGS; do
      # do not tag if the tag is the same as the image tag
      if [ "$T" != "$APPLICATION_IMAGE" ]; then
        log "Tagging $APPLICATION_IMAGE as $T"
        docker tag "$APPLICATION_IMAGE" "$T"
      fi

      # if push is enabled, push the image to the registry
      if [ ! -z "$PUSH" ]; then
        log "Pushing $T to registry"
        docker push "$T"
      fi
    done
  else
    # create cmd for buildx, repeat --tag for each tag
    local CMD=""
    for T in $FULL_TAGS; do
      CMD+="--tag $T "
    done
    # if push is enabled, add --push
    if [ ! -z "$PUSH" ]; then
      CMD+="--push "
    fi
    # if no cache is enabled, add --no-cache
    if [ ! -z "$NO_CACHE" ]; then
      CMD+="--no-cache "
    fi
  
    # buildx build command
    docker buildx build \
      --platform $PLATFORM \
      --load --pull $CMD \
      $@
  fi
}

# --------------------------------------------------------------------

if [ -z "$USE_BUILDX" ] || [ "$USE_BUILDX" != "1" ]; then
  log "Using docker build"
else
  log "Using docker buildx"
fi

if [ -z "$PLATFORM" ]; then
  # use system architecture if not specified
  UNAME="$(uname -m)"
  if [ "$UNAME" == "x86_64" ]; then
    PLATFORM="linux/amd64"
  elif [ "$UNAME" == "aarch64" ] || [ "$UNAME" == "arm64" ]; then
    PLATFORM="linux/arm64"
  elif [ "$UNAME" == "armv7l" ]; then
    PLATFORM="linux/arm/v7"
  else
    log "Unknown architecture: $UNAME"
    exit 1
  fi
fi
log "Using platform: $PLATFORM"

if [ -z "$REPOSITORY" ]; then
  REPOSITORY="ghcr.io/m1k1o/neko"
fi
log "Using repository: $REPOSITORY"

if [ -z "$TAG" ]; then
  # if git is available, use the latest tag from git
  if [ -d ".git" ] && [ -x "$(command -v git)" ]; then
    # get the latest version tag from git (v*.*.*)
    GIT_VER=$(git describe --tags --exact-match --match "v*.*.*" 2>/dev/null || true)
    if [ ! -z "$GIT_VER" ]; then
      # split to major, minor and patch
      IFS='.' read -r MAJOR MINOR PATCH <<< "${GIT_VER#v}"
      # append MAJOR.MINOR.PATCH, MAJOR.MINOR and MAJOR to the tags
      TAGS=("$MAJOR.$MINOR.$PATCH" "$MAJOR.$MINOR" "$MAJOR")
    #else
    #  # if no version tag is found, use the latest commit hash
    #  GIT_VER=$(git rev-parse HEAD 
    #  TAGS=("sha-$GIT_VER")
    fi
  fi
  TAG="latest"
  TAGS=("$TAG" "${TAGS[@]}")
fi

for T in "${TAGS[@]}"; do
  log "Using tag: $T"
done

if [ -z "$FLAVOR" ]; then
  log "No flavor specified, building without flavor"
else
  log "Using flavor: $FLAVOR"
fi

if [ -z "$BASE_IMAGE" ]; then
  if [ -z "$FLAVOR" ]; then
    BASE_IMAGE="$REPOSITORY/base:$TAG"
  else
    BASE_IMAGE="$REPOSITORY/$FLAVOR-base:$TAG"
  fi
fi

# --------------------------------------------------------------------

if [ ! -z "$APPLICATION" ]; then
  log "Building application: $APPLICATION"
  log "Using base image: $BASE_IMAGE"

  # check if application directory exists
  APPLICATION_DIR="apps/$APPLICATION"
  if [ ! -d "$APPLICATION_DIR" ]; then
    log "Application directory $APPLICATION_DIR does not exist."
    exit 1
  fi

  # flavor is specified, append it to the image name and Dockerfile
  APPLICATION_IMAGE="$REPOSITORY/$APPLICATION:$TAG"
  APPLICATION_DOCKERFILE="apps/$APPLICATION/Dockerfile"
  if [ ! -z "$FLAVOR" ]; then
    APPLICATION_IMAGE="$REPOSITORY/$FLAVOR-$APPLICATION"
    # if application flavor is specified and Dockerfile exists, use it
    if [ -f "$APPLICATION_DIR/Dockerfile.$FLAVOR" ]; then
      APPLICATION_DOCKERFILE="$APPLICATION_DIR/Dockerfile.$FLAVOR"
    fi
  fi

  prompt "Are you sure you want to build $APPLICATION_IMAGE from $APPLICATION_DOCKERFILE?"

  log "Building $APPLICATION_IMAGE image from $APPLICATION_DOCKERFILE"
  build_image $APPLICATION_IMAGE \
    --build-arg="BASE_IMAGE=$BASE_IMAGE" \
    -f $APPLICATION_DOCKERFILE \
    $APPLICATION_DIR

  exit 0
fi

# --------------------------------------------------------------------

prompt "Are you sure you want to build $BASE_IMAGE?"

RUNTIME_DOCKERFILE="Dockerfile"
if [ ! -z "$FLAVOR" ] && [ -f "runtime/Dockerfile.$FLAVOR" ]; then
  RUNTIME_DOCKERFILE="Dockerfile.$FLAVOR"
fi

log "Building base image: $BASE_IMAGE"
docker run --rm -i \
  -v "$(pwd)":/src \
  -e "RUNTIME_DOCKERFILE=$RUNTIME_DOCKERFILE" \
  --workdir /src \
  --entrypoint go \
  golang:1.24-bullseye \
  run utils/docker/main.go \
  -i Dockerfile.tmpl -client "$CLIENT_DIST" | build_image $BASE_IMAGE -f - .
